#include "Msger.h"

namespace x2lib
{
    class MsgPool;

    /****************************************************************Msger*****************************************************************/

    void Msger::Post(Msger::MsgX* pMsg)
    {
        assert(pMsg->isPost);
        m_mtxQuepMsging.Lock();
        m_quepMsging.push(pMsg);
        m_mtxQuepMsging.Unlock();
    }

    const Msger::MsgX* Msger::Send(Msger::MsgX* pMsg, bool isDirect)
    {
        assert(!pMsg->isPost);

        if (isDirect)
        {
            m_mtxMappExecutor.Lock();
            auto itSet = m_mspExecutor.find(pMsg->uMsgId);
            if (itSet != m_mspExecutor.end())
            {
                for (auto& it : itSet->second)
                {
                    if (it->OnMsgExec(pMsg)) { break; }
                }
            }
            m_mtxMappExecutor.Unlock();
        }
        else
        {
            m_mtxQuepMsging.Lock();
            m_quepMsging.push(pMsg);
            m_mtxQuepMsging.Unlock();
            m_SigMsging.Acquire();
        }

        return pMsg;
    }

    void Msger::Tell(MsgX* pMsg, bool isDirect)
    {
        if (isDirect)
        {
            m_mtxQuepMsgend.Lock();
            m_quepMsgend.push(pMsg);
            m_mtxQuepMsgend.Unlock();
        }
        else
        {
            m_mtxMappReviewer.Lock();
            auto item = m_mspReviewer.find(pMsg->uMsgId);
            if (item != m_mspReviewer.end())
            {
                for (auto& it : item->second)
                {
                    if (it->OnMsgTell(pMsg)) { break; }
                }
            }
            m_mtxMappReviewer.Unlock();
            MsgPool::GetInst()->UnTake(pMsg);
        }
    }

    Msger::Msger(bool inTwoThread)
        //xUtils::ISingleton<Msger, bool>(inTwoThread),
    {
        m_isRunning = false;
        m_nExit = 0;
        m_inTwoThread = inTwoThread;
    }

    Msger::~Msger()
    {
        m_isRunning = false;
        m_nExit = 0;
    }

    bool Msger::initInst()
    {
        MsgPool::InitInst();
        if (m_inTwoThread)
        {
            ID_CYLID1 = Engine::Ignite((void*)1);
            ID_CYLID2 = Engine::Ignite((void*)2);
        }
        else
        {
            ID_CYLID0 = Engine::Ignite((void*)0);
        }
        return true;
    }

    void Msger::unInitInst()
    {
        if (m_inTwoThread)
        {
            Engine::Stall(ID_CYLID1);
            Engine::Stall(ID_CYLID2);
        }
        else
        {
            Engine::Stall(ID_CYLID0);
        }
        MsgPool::UnInitInst();
    }

    bool/*空轮询*/ Msger::notifyExecutor()
    {
        MsgX* pMsging = nullptr;
        m_mtxQuepMsging.Lock();
        if (m_quepMsging.size() > 0)
        {
            pMsging = m_quepMsging.front();
            m_quepMsging.pop();
        }
        m_mtxQuepMsging.Unlock();

        if (pMsging)
        {
            m_mtxMappExecutor.Lock();
            auto itSet = m_mspExecutor.find(pMsging->uMsgId);
            bool isBreak = (itSet == m_mspExecutor.end());
            if (!isBreak)
            {
                for (auto& it : itSet->second)
                {
                    isBreak = it->OnMsgExec(pMsging);
                    if (isBreak) { break; }
                }
            }
            m_mtxMappExecutor.Unlock();

            if (pMsging->isPost)
            {
                if (!isBreak)
                {
                    if (pMsging->auTell) { this->Tell(pMsging); }
                    else { MsgPool::GetInst()->UnTake(pMsging); }
                }
                else { MsgPool::GetInst()->UnTake(pMsging); }
            }
            else
            {
                m_SigMsging.Release(1);
            }

            return false;
        }
        return true;
    };

    bool/*空轮询*/ Msger::notifyReviewer()
    {
        MsgX* pMsgend = nullptr;
        m_mtxQuepMsgend.Lock();
        if (m_quepMsgend.size() > 0)
        {
            pMsgend = m_quepMsgend.front();
            m_quepMsgend.pop();
        }
        m_mtxQuepMsgend.Unlock();

        if (pMsgend)
        {
            m_mtxMappReviewer.Lock();
            auto item = m_mspReviewer.find(pMsgend->uMsgId);
            if (item != m_mspReviewer.end())
            {
                for (auto& it : item->second)
                {
                    if (it->OnMsgTell(pMsgend)) { break; }
                }
            }
            m_mtxMappReviewer.Unlock();
            MsgPool::GetInst()->UnTake(pMsgend);
            return false;
        }
        return true;
    };

    void Msger::onEngineExec(uint32_t id, void* vData)
    {
        intz_t iMode = (intz_t)vData;
        m_isRunning = true;

        uint8_t nIdle = 0;
        do
        {
            if (nIdle > 100)
            {
                std::this_thread::sleep_for(std::chrono::milliseconds(200));
                nIdle = 0;
            }
            nIdle++;

            if (iMode != 2)
            {
                if (!notifyExecutor()) { nIdle = 0; }
            }
            if (iMode != 1)
            {
                if (!notifyReviewer()) { nIdle = 0; }
            }
        } while (m_isRunning);
    }
    void Msger::onEngineExit(uint32_t id, void* vData)
    {
        m_isRunning = false;
    }


    /****************************************************************Msger::MsgX*****************************************************************/
    Msger::MsgX::MsgX(uint16_t uMsgId, bool isPost, bool auTell)
        : uMsgId(uMsgId), isTake(false), isPost(isPost), auTell(isPost && auTell), uDbgId(MsgX::__gen_DbgId__())
    {
        this->iError = 0;
    }

    uint32_t Msger::MsgX::__gen_DbgId__()
    {
        static uint32_t __uDbgId_base__ = 1000; 
        return ++__uDbgId_base__; 
    };

    bool Msger::IExecutor::MsgBind(IExecutor* pExecutor, bool addMode, ...)
    {
        Msger* pMsger = Msger::GetInst();
        if (!pMsger) { return false; }

        va_list body;
        va_start(body, addMode);
        uint16_t uMsgId = va_arg(body, uint16_t);
        pMsger->m_mtxMappExecutor.Lock();
        if (uMsgId == Msg_None_Id)
        {
            if (!addMode)
            {
                for (auto& it : pMsger->m_mspExecutor)
                {
                    it.second.erase(pExecutor);
                }
            }
        }
        else
        {
            do
            {
                if (uMsgId == Msg_None_Id) { break; }
                auto& it = pMsger->m_mspExecutor[uMsgId];
                if (addMode) { it.emplace(pExecutor); }
                else { it.erase(pExecutor); }
                uMsgId = va_arg(body, uint16_t);
            } while (true);
        }
        pMsger->m_mtxMappExecutor.Unlock();
        va_end(body);
        return true;
    }

    bool Msger::IReviewer::MsgBind(Msger::IReviewer* pReviewer, bool addMode, ...)
    {
        Msger* pMsger = Msger::GetInst();
        if (!pMsger) { return false; }

        va_list body;
        va_start(body, addMode);
        uint16_t uMsgId = va_arg(body, uint16_t);
        pMsger->m_mtxMappReviewer.Lock();
        if (uMsgId == Msg_None_Id)
        {
            if (!addMode)
            {
                for (auto& it : pMsger->m_mspReviewer)
                {
                    it.second.erase(pReviewer);
                }
            }
        }
        else
        {
            do
            {
                if (uMsgId == Msg_None_Id) { break; }
                auto& it = pMsger->m_mspReviewer[uMsgId];
                if (addMode) { it.emplace(pReviewer); }
                else { it.erase(pReviewer); }
                uMsgId = va_arg(body, uint16_t);
            } while (true);
        }
        pMsger->m_mtxMappReviewer.Unlock();
        va_end(body);
        return true;
    }


    /****************************************************************MsgPool*****************************************************************/
    // 一般只适合Send消息调用，但不禁止Post消息调用，因为可能用户在Post和Tell都设置了autoUnTake=false
    void MsgPool::UnTake(Msger::MsgX* msg)
    {
        m_mtxPool.Lock();
        if (m_setMsg.count(msg) == 1)
        {
            const_cast<bool&>(msg->isTake) = false;
            msg->Reset(); // 防止调用者在UnTake后非法操作
        }
        m_mtxPool.Unlock();
    }
}
